﻿using IdentityModel.Client;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
using System.Net.Sockets;

namespace WebAppDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    
    public class IdentityController : ControllerBase
    {
        string baseAddress = "http://localhost:5001";
        //发现文档地址： http://localhost:5001/.well-known/openid-configuration
        //授权码模式：https://blog.csdn.net/sD7O95O/article/details/110729528
        //授权码模式：https://www.cnblogs.com/easy5weikai/p/17199370.html
        //配置scope：https://www.cnblogs.com/xhznl/p/13223964.html

        /// <summary>
        /// 测试
        /// </summary>
        /// <returns></returns>
        [HttpGet("Test")]
        public IActionResult GetTest()
        {
            return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
        }

        /// <summary>
        /// 获取时间
        /// </summary>
        /// <returns></returns>
        [HttpGet("DateTime")]
        public async Task<string> GetDateTime()
        { 
            return await Task.FromResult(DateTime.Now.ToString());
        }

        //public async Task<IActionResult> CallApi()
        //{
        //    var accessToken = await HttpContext.GetTokenAsync("access_token");

        //    var client = new HttpClient();
        //    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        //    var content = await client.GetStringAsync("https://localhost:6001/identity");

        //    //ViewBag.Json = JArray.Parse(content).ToString();
        //    //return View("json");

        //    return Task.FromResult(content);
        //}

        [HttpGet("CallApi")]
        public async Task<string> CallApi(string accessToken)
        {
            //启用了 SaveTokens，ASP.NET Core 将自动将结果访问和刷新令牌存储在身份验证会话中
            //var accessToken = await HttpContext.GetTokenAsync("access_token");
            // 调用api
            var apiClient = new HttpClient();
            //apiClient.SetBearerToken(tokenResponse.AccessToken);
            apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            var response = await apiClient.GetAsync("https://localhost:5240/identity");
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
                return response.StatusCode.ToString();
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                Console.WriteLine(JArray.Parse(content));
                return content;
            }
        }

        /// <summary>
        /// 开放获取token API 接口
        /// </summary>
        /// <returns></returns>
        [HttpGet("GetToken")]
        public async Task<string> GetToken(string clientId = "client", string secret= "secret", string scope = "api1")
        {
            var client = new HttpClient();
            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                Address = "http://localhost:5001/connect/token",
                ClientId = "client",
                ClientSecret = "secret",
                Scope = "api1",
                UserName = "Admin",
                Password = "123456",
            });

            if (tokenResponse.IsError)
            {
                return tokenResponse.Error;
            }

            return tokenResponse.AccessToken;

        }

        #region 客户端模式
        /// <summary>
        /// 访问获取客户端模式Token
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="secret"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        [HttpGet("ClientToken")]
        public async Task<string> GetClientToken(string clientId = "client", string secret = "secret", string scope = "clientScope")
        {
            var client = new HttpClient();
            var content = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("grant_type", "client_credentials"),
                new KeyValuePair<string, string>("client_id", clientId),
                new KeyValuePair<string, string>("client_secret", secret),
                new KeyValuePair<string, string>("scope", scope)
            });

            //var response = await client.PostAsync("http://your-identityserver-url/connect/token", content);
            var response = await client.PostAsync("http://localhost:5001/connect/token", content);
            var responseString = await response.Content.ReadAsStringAsync();

            // 处理响应以获取访问令牌  
            // 注意：这里应该添加错误处理和令牌解析逻辑  

            return responseString;
        }

        /// <summary>
        /// 客户端模式获取Token
        /// </summary>
        /// <param name="clientId">客户端Id</param>
        /// <param name="secret">客户端密码</param>
        /// <param name="scope">scope名称</param>
        /// <returns></returns>
        [HttpGet("RequestClientCredentialsToken")]
        public async Task<string> RequestClientCredentialsToken(string clientId = "client", string secret = "secret", string scope = "clientScope")
        {
            // 从元数据中发现端点
            var client = new HttpClient();
            Console.WriteLine($"************访问发现文档************");
            var disco = await client.GetDiscoveryDocumentAsync(baseAddress);
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return disco.Error;
            }
            // 请求令牌
            Console.WriteLine($"************客户端模式获取Token************");
            var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = clientId,
                ClientSecret = secret,
                Scope = scope
            });

            if (tokenResponse.IsError)
            {
                Console.WriteLine(tokenResponse.Error);
                return tokenResponse.Error;
            }

            Console.WriteLine(tokenResponse.Json);
            return tokenResponse.AccessToken;

        }

        /// <summary>
        /// 客户端模式访问Api
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="secret"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        [HttpGet("RequestClientCredentials")]
        public async Task<string> RequestClientCredentials(string clientId = "client", string secret = "secret", string scope = "clientScope")
        {
            // 从元数据中发现端点
            var client = new HttpClient();
            Console.WriteLine($"************访问发现文档************");
            var disco = await client.GetDiscoveryDocumentAsync(baseAddress);
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return disco.Error;
            }
            // 请求令牌
            Console.WriteLine($"************客户端模式获取Token************");
            var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = clientId,
                ClientSecret = secret,
                Scope = scope
            });

            if (tokenResponse.IsError)
            {
                Console.WriteLine(tokenResponse.Error);
                return tokenResponse.Error;
            }

            Console.WriteLine(tokenResponse.Json);
            string accessToken = tokenResponse.AccessToken;

            Console.WriteLine($"************带上Token访问Api资源************");
            //启用了 SaveTokens，ASP.NET Core 将自动将结果访问和刷新令牌存储在身份验证会话中
            //var accessToken = await HttpContext.GetTokenAsync("access_token");
            // 调用api
            var apiClient = new HttpClient();
            //apiClient.SetBearerToken(accessToken);
            apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            var response = await apiClient.GetAsync("http://localhost:5002/api/User/ServiceDateTime"); //访问5002站点资源
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
                return response.StatusCode.ToString();
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                Console.WriteLine(content);//JArray.Parse(content)
                return content;
            }
        }
        #endregion

        #region 密码模式

        /// <summary>
        /// 密码模式获取用户信息
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="secret"></param>
        /// <param name="scope"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <returns></returns>
        [HttpGet("RequestPasswordPattern")]
        public async Task<string> RequestPassword(string clientId = "PassPattern", string secret = "secret", string scope = "PasswordScope openid profile"
            , string userName = "TestUserA", string passWord = "password")
        {

            // 从元数据中发现端点
            var client = new HttpClient();
            Console.WriteLine($"************访问发现文档************");
            var disco = await client.GetDiscoveryDocumentAsync(baseAddress);
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return disco.Error;
            }
            // 请求令牌
            Console.WriteLine($"************密码模式获取Token************");
            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = clientId,
                ClientSecret = secret,
                Scope = scope,// 以空格间隔摆放
                UserName = userName,
                Password = passWord
            });

            if (tokenResponse.IsError)
            {
                Console.WriteLine(tokenResponse.Error);
                return tokenResponse.Error;
            }

            Console.WriteLine(tokenResponse.Json);
            string accessToken = tokenResponse.AccessToken;

            {
                Console.WriteLine($"************带上Token去授权服务器获取用户信息************");

                Console.WriteLine($"获取用户信息的Api地址：{disco.UserInfoEndpoint}");
                // 调用api
                var apiClient = new HttpClient();
                //apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

                var responseUserInfo = await apiClient.GetUserInfoAsync(new UserInfoRequest
                {
                    Token = accessToken,
                    Address = disco.UserInfoEndpoint,
                });

                if (responseUserInfo.IsError)
                {
                    Console.WriteLine(responseUserInfo.Error);
                    return responseUserInfo.Error;
                }

                Console.WriteLine(responseUserInfo.Raw);

            }

            {
                Console.WriteLine($"************带上Token访问带admin角色的Api资源************");
                //启用了 SaveTokens，ASP.NET Core 将自动将结果访问和刷新令牌存储在身份验证会话中
                //var accessToken = await HttpContext.GetTokenAsync("access_token");
                // 调用api
                var apiClient = new HttpClient();
                //apiClient.SetBearerToken(accessToken);
                apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                var response = await apiClient.GetAsync("http://localhost:5002/api/User/GetAdminAuthTest"); //访问5002站点资源
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.StatusCode);
                    return response.StatusCode.ToString();
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();//不加Token和Authorize,不报错，返回值为[]
                    Console.WriteLine(content);//JArray.Parse(content)
                }
            }

            {
                Console.WriteLine($"************带上Token访问Api资源************");
                //启用了 SaveTokens，ASP.NET Core 将自动将结果访问和刷新令牌存储在身份验证会话中
                //var accessToken = await HttpContext.GetTokenAsync("access_token");
                // 调用api
                var apiClient = new HttpClient();
                //apiClient.SetBearerToken(accessToken);
                apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                var response = await apiClient.GetAsync("http://localhost:5002/api/User/UserInfo"); //访问5002站点资源
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.StatusCode);
                    return response.StatusCode.ToString();
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();//不加Token和Authorize,不报错，返回值为[]
                    Console.WriteLine(content);//JArray.Parse(content)
                    return content;
                }
            }

        }

        #endregion

        #region 授权码模式

        //授权码模式流程是:用户访问客户端，客户端调转到认证服务器登录页面，让用户输入用户名和密码，并点击授权后，得到一个code(授权码)，并放在回调URL中，
        //客户端再从这个回调的URL中解析到得到code(授权码)，再通过code(授权码)向服务器发送请求获取 access_token。
        // 认证服务器得有一个界面:把 Quickstart、Views、wwwroot 三个文件夹复制 项目根目录下。由于 Quickstart UI 使用了 AspNet Core 的 MVC 框架，所以得在 Program.cs 开启 MVC 框架：

        /// <summary>
        /// 授权码模式，apia请求失败
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="secret"></param>
        /// <param name="scope"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <returns></returns>
        [HttpGet("Code")]
        public async Task<string> RequestCode(string clientId = "PassPattern", string secret = "secret", string scope = "PasswordScope openid profile"
            , string userName = "TestUserA", string passWord = "password")
        {
            // 从元数据中发现端点
            var client = new HttpClient();
            Console.WriteLine($"************访问发现文档************");
            var disco = await client.GetDiscoveryDocumentAsync(baseAddress);
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return disco.Error;
            }

            {
                Console.WriteLine($"************去授权服务器获取code信息************");

                Console.WriteLine($"获取用户信息的Api地址：{disco.UserInfoEndpoint}");
                // 调用api
                var apiClient = new HttpClient();
                //var content = new FormUrlEncodedContent(new[]
                //{
                //    new KeyValuePair<string, string>("grant_type", "client_credentials"),
                //    new KeyValuePair<string, string>("client_id", clientId),
                //    new KeyValuePair<string, string>("client_secret", secret),
                //    new KeyValuePair<string, string>("scope", scope)
                //});
                var authorizeUrl = "http://localhost:5001/connect/authorize";
                var redirectUris = "https://localhost:5002/signin-oidc";
                var state = Guid.NewGuid().ToString();
                var postLogoutRedirectUris = "https://localhost:5002/signout-callback-oidc";


                var strRequestAuthorizeUrl = $"{disco.AuthorizeEndpoint}?response_type=code&client_id={clientId}&scope={scope}&redirect_uri={redirectUris}&state={state}";
                var strRequestAuthorizeUrl2 = $"{"http://localhost:5001/oauth/authorize"}?response_type=code&client_id={clientId}&scope={scope}&redirect_uri={redirectUris}&state={state}";
                var response2 = await apiClient.GetAsync(strRequestAuthorizeUrl2); //访问5002站点资源
                var response = await apiClient.GetAsync(strRequestAuthorizeUrl); //访问5002站点资源
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.StatusCode);
                    return response.StatusCode.ToString();
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();//不加Token和Authorize,不报错，返回值为[]
                    Console.WriteLine(content);//JArray.Parse(content)
                }

            }
            string accessToken = string.Empty;

            {
                Console.WriteLine($"************向认证服务器申请访问令牌信息************");
                var tokenUrl = "http://localhost:5001//connect/token";
                var redirectUris = "https://localhost:5002/signin-oidc";
                var strRequestTokenUrl = $"{disco.TokenEndpoint}?grant_type=authorization_code&client_id={clientId}&client_secret={secret}&code={"hdh922"}&redirect_uri={redirectUris}";
                // 调用api
                var apiClient = new HttpClient();
                var response = await apiClient.GetAsync(strRequestTokenUrl); //访问5002站点资源
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.StatusCode);
                    return response.StatusCode.ToString();
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(content);//JArray.Parse(content)
                    //{
                    //      //返回值
                    //    "access_token":"2YotnFZFEjr1zCsicMWpAA",
                    //    "token_type":"Bearer",
                    //    "expires_in":3600,
                    //    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
                    //    "scope":"read",
                    //    "example_parameter":"example_value"
                    //}

                    accessToken = "";
                }
            }

            {
                Console.WriteLine($"************访问令牌过期后，刷新访问令牌信息************");
                // 调用api
                var apiClient = new HttpClient();
                apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                Console.WriteLine($"************向认证服务器申请访问令牌信息************");
                var tokenUrl = "http://localhost:5001//connect/token";
                var redirectUris = "https://localhost:5002/signin-oidc";
                var strRequestRefreshTokenUrl = $"{disco.TokenEndpoint}?grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA";
                
                var response = await apiClient.GetAsync(strRequestRefreshTokenUrl); 
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.StatusCode);
                    return response.StatusCode.ToString();
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();//不加Token和Authorize,不报错，返回值为[]
                    Console.WriteLine(content);//JArray.Parse(content)
                }
            }
            
            return accessToken;
        }

        #endregion


        ///// <summary>
        ///// 获取API资源。
        ///// </summary>
        ///// <returns></returns>
        //public async Task<IActionResult> ApiData()
        //{
        //    //获取accessToken
        //    var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
        //    //请求API资源
        //    var httpClient = new HttpClient();
        //    //将获取到的AccessToken以Bearer的方案设置在请求头中
        //    httpClient.SetBearerToken(accessToken);
        //    //向API资源服务器请求受保护的API
        //    var data = await httpClient.GetAsync("https://localhost:6011/api/customer/getlist");
        //    if (data.IsSuccessStatusCode)
        //    {
        //        var r = await data.Content.ReadAsStringAsync();
        //        ViewBag.ApiData = r;
        //    }
        //    else
        //    {
        //        ViewBag.ApiData = "获取API数据失败。";
        //    }
        //    return View();
        //}

        #region 触发 Windows 身份验证

        /// <summary>
        /// 触发 Windows 身份验证
        /// </summary>
        /// <param name="returnUrl"></param>
        /// <returns></returns>

        //private async Task<IActionResult> ChallengeWindowsAsync(string returnUrl)
        //{
        //    // 查看是否已请求 Windows 身份验证并成功
        //    var result = await HttpContext.AuthenticateAsync("Windows");
        //    if (result?.Principal is WindowsPrincipal wp)
        //    {
        //        // 我们将发出外部 cookie，
        //        // 然后将用户重定向回外部回调，
        //        // 本质上，将 windows auth 视为与任何其他外部身份验证机制相同
        //        var props = new AuthenticationProperties()
        //        {
        //            RedirectUri = Url.Action("Callback"),
        //            Items =
        //    {
        //        { "returnUrl", returnUrl },
        //        { "scheme", "Windows" },
        //    }
        //        };

        //        var id = new ClaimsIdentity("Windows");

        //        // sid 是一个很好的子值
        //        id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.FindFirst(ClaimTypes.PrimarySid).Value));

        //        // 帐户名称是我们最接近显示名称的名称
        //        id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));

        //        // 添加组作为声明 —— 如果组数太大，请小心
        //        var wi = wp.Identity as WindowsIdentity;

        //        // 将组 SID 转换为显示名称
        //        var groups = wi.Groups.Translate(typeof(NTAccount));
        //        var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
        //        id.AddClaims(roles);


        //        await HttpContext.SignInAsync(
        //            IdentityServerConstants.ExternalCookieAuthenticationScheme,
        //            new ClaimsPrincipal(id),
        //            props);
        //        return Redirect(props.RedirectUri);
        //    }
        //    else
        //    {
        //        // 触发 windows auth
        //        // 因为 windows auth 不支持重定向 uri，
        //        // 当我们调用 challenge 时会重新触发这个 URL
        //        return Challenge("Windows");
        //    }
        //} 
        #endregion

    }

}
